﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace SharePointStack.SPBlogTemplate.ArchiveSummary
{
    public partial class ArchiveSummaryUserControl : UserControl
    {
        static object _lock =  new object();
        List<SPBlogPost> postsBuffer = new List<SPBlogPost>();
        List<int> years = new List<int>();
        string[] months = { "January", "February", "March", "April", "May", "June", "July", "August", 
                              "September", "October", "November", "December" };
        bool duplicates;

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                try
                {
                    //I chose "using" to automaticaly dispose SharePoint objects
                    using (SPSite blogSiteCollection = SPContext.Current.Site)
                    {
                        using (SPWeb blogWeb = blogSiteCollection.OpenWeb(SPContext.Current.Web.ServerRelativeUrl))
                        {
                            SPList blogPosts = blogWeb.Lists["Posts"];

                            //Set the header link
                            ViewArchive.HRef = blogPosts.DefaultViewUrl;

                            SPQuery queryPosts = new SPQuery();
                            
                            //CAML Query that returns only published posts and orders them by date
                            queryPosts.Query = "<OrderBy><FieldRef Name='PublishedDate'/></OrderBy>" + 
                                                "<Where><Eq><FieldRef Name='_ModerationStatus' />" + 
                                                "<Value Type='ModStat'>0</Value></Eq></Where>";
                            
                            //Pull the query results directly from the Cache, we don't want to query
                            //SharePoint everytime the Archives Summary Web Part runs. This practice increases 
                            //the performance and comes very handy if the user is an active blogger.
                            SPListItemCollection publishedPosts = (SPListItemCollection)Cache["PublishedPosts"];
                            
                            //If the query results are not avilable in the Cache, then we need to execute the query and
                            //store the results in the Cache for the next request.
                            //The Cache is set with Sliding Expiration of 1 day.
                            if (publishedPosts == null)
                            {
                                //Since SPWeb is not thread safe, we need to place a lock.
                                lock (_lock)
                                {
                                    //Ensure that the data was not loaded by a concurrent thread while waiting for lock.
                                    publishedPosts = blogPosts.GetItems(queryPosts);
                                    Cache.Add("PublishedPosts", publishedPosts, null, Cache.NoAbsoluteExpiration, 
                                        TimeSpan.FromDays(1), CacheItemPriority.High, null);
                                }
                            }

                            //Load all published posts into the postsBuffer. The query results will not be available
                            //outside the "using" block.
                            foreach (SPListItem post in publishedPosts)
                                postsBuffer.Add(new SPBlogPost(post["Title"].ToString(), 
                                    DateTime.Parse(post["PublishedDate"].ToString()).Month.ToString(), 
                                    DateTime.Parse(post["PublishedDate"].ToString()).Year.ToString()));
                        }
                    }

                    //Provision years
                    foreach (SPBlogPost post in postsBuffer)
                        years.Add(int.Parse(post.PublishingYear));

                    //Make sure we only have distinct years that are sorted in descending order
                    var yearsList = years.Distinct().ToList();
                    yearsList.Sort();
                    yearsList.Reverse();

                    //Add the years to the Tree View
                    foreach (int year in yearsList)
                        ArchiveSummaryTree.Nodes.Add(new TreeNode(year.ToString()));

                    //Find out which months have posts in each year and add them to the Tree View as ChildNodes
                    foreach (TreeNode year in ArchiveSummaryTree.Nodes)
                    {
                        for (int i = 12; i >= 1; i--)
                        {
                            foreach (SPBlogPost post in postsBuffer)
                            {
                                if (post.PublishingMonth == i.ToString() && post.PublishingYear == year.Text)
                                {
                                    duplicates = false;
                                    foreach (TreeNode item in year.ChildNodes)
                                    {
                                        //Check for any duplicate month entries.
                                        //This will become an issue if the user posts more than one post in a month time.
                                        if (item.Text.ToLower() == months[int.Parse(post.PublishingMonth) - 1].ToLower())
                                            duplicates = true;
                                    }
                                    if (!duplicates)
                                        year.ChildNodes.Add(new TreeNode(months[int.Parse(post.PublishingMonth) - 1], 
                                            post.PublishingMonth, null, null, null));
                                }
                            }
                        }
                    }

                    //Expand the latest year node
                    if (ArchiveSummaryTree.Nodes[0] != null)
                        ArchiveSummaryTree.Nodes[0].Expand();
                }
                catch (Exception ex)
                {
                    //Print the error messege to the user using a Literal
                    ArchiveSummaryErrors.Text = "<img src='" + SPContext.Current.Site + 
                        "_layouts/images/SharePointStack/SPBlogTemplate/error.png' alt='Error' style='display: block;' />" + 
                        "&nbsp;<font color='#ff0000' size='12'>" + ex.Message + "</font>";
                }
                finally
                {
                    //Objects will be disposed during the next Garbage Collector run
                    postsBuffer = null;
                    years = null;
                }
            }
        }

        protected void ArchiveSummaryTree_SelectedNodeChanged(object sender, EventArgs e)
        {
            TreeView summaryLinks = (TreeView)sender;

            //Make sure that this is a month node, we don't want to handle a year node selection change
            if (summaryLinks.SelectedNode.Parent != null)
            {
                string month = string.Empty, year = string.Empty;
                month = summaryLinks.SelectedNode.Value;
                year = summaryLinks.SelectedNode.Parent.Value;

                //Build the date strings to be converted later on to ISO8601 dates
                string startDate = month + "/1/" + year;
                string endDate = startDate;

                //Convert the dates and set the range to be a one month time span
                startDate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Parse(startDate));
                endDate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Parse(startDate).AddMonths(1));

                //Build the URL and set the Query String parameters for redirection to the Date.aspx page
                string url = SPContext.Current.Site + SPContext.Current.Web.ServerRelativeUrl + 
                    "/Lists/Posts/Date.aspx?StartDateTime=" + startDate + "&EndDateTime=" + endDate + 
                    "&LMY=" + months[int.Parse(month) - 1] + ", " + year;

                summaryLinks.Dispose();

                //SharePoint will add "SPSite Url=" to the URL we built. I used Substring to remove it.
                Response.Redirect(url.Substring(11));
            }
        }
    }
}